/*
 * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
 * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 */

package java.lang.invoke;

/**
 * <p>
 * {@code SwitchPoint} 是一个可以向其他线程发布状态转换的对象。
 * 切换点最初处于<em>有效</em>状态，但可以在任何时候
 * 更改为<em>无效</em>状态。无效化不能逆转。
 * 切换点可以将方法句柄的<em>保护对</em>组合成
 * <em>保护委托器</em>。
 * 保护委托器是一个方法句柄，它委托给其中一个旧方法句柄。
 * 切换点的状态决定了两者中哪一个获得委托。
 * <p>
 * 单个切换点可用于控制任意数量的方法句柄。
 * （因此，间接地，它可以控制任意数量的调用站点。）
 * 这是通过使用单个切换点作为工厂来实现的，将
 * 任意数量的保护方法句柄对组合成保护委托器。
 * <p>
 * 当从保护对创建保护委托器时，该对
 * 被包装在新的方法句柄 {@code M} 中，
 * 该方法句柄与创建它的切换点永久关联。
 * 每对包含一个目标 {@code T} 和一个回退 {@code F}。
 * 当切换点有效时，对 {@code M} 的调用被委托给 {@code T}。
 * 无效化后，调用被委托给 {@code F}。
 * <p>
 * 无效化是全局和即时的，就像切换点包含一个
 * 在每次调用 {@code M} 时都会查询的 volatile boolean 变量。
 * 无效化也是永久的，这意味着切换点
 * 只能更改状态一次。
 * 切换点在无效化后将始终委托给 {@code F}。
 * 此时 {@code guardWithTest} 可能会忽略 {@code T} 并返回 {@code F}。
 * <p>
 * Here is an example of a switch point in action:
 * <pre>{@code
 * MethodHandle MH_strcat = MethodHandles.lookup()
 *     .findVirtual(String.class, "concat", MethodType.methodType(String.class, String.class));
 * SwitchPoint spt = new SwitchPoint();
 * assert(!spt.hasBeenInvalidated());
 * // the following steps may be repeated to re-use the same switch point:
 * MethodHandle worker1 = MH_strcat;
 * MethodHandle worker2 = MethodHandles.permuteArguments(MH_strcat, MH_strcat.type(), 1, 0);
 * MethodHandle worker = spt.guardWithTest(worker1, worker2);
 * assertEquals("method", (String) worker.invokeExact("met", "hod"));
 * SwitchPoint.invalidateAll(new SwitchPoint[]{ spt });
 * assert(spt.hasBeenInvalidated());
 * assertEquals("hodmet", (String) worker.invokeExact("met", "hod"));
 * }</pre>
 * <p style="font-size:smaller;">
 * <em>Discussion:</em>
 * Switch points are useful without subclassing.  They may also be subclassed.
 * This may be useful in order to associate application-specific invalidation logic
 * with the switch point.
 * Notice that there is no permanent association between a switch point and
 * the method handles it produces and consumes.
 * The garbage collector may collect method handles produced or consumed
 * by a switch point independently of the lifetime of the switch point itself.
 * <p style="font-size:smaller;">
 * <em>Implementation Note:</em>
 * A switch point behaves as if implemented on top of {@link MutableCallSite},
 * approximately as follows:
 * <pre>{@code
 * public class SwitchPoint {
 *     private static final MethodHandle
 *         K_true  = MethodHandles.constant(boolean.class, true),
 *         K_false = MethodHandles.constant(boolean.class, false);
 *     private final MutableCallSite mcs;
 *     private final MethodHandle mcsInvoker;
 *     public SwitchPoint() {
 *         this.mcs = new MutableCallSite(K_true);
 *         this.mcsInvoker = mcs.dynamicInvoker();
 *     }
 *     public MethodHandle guardWithTest(
 *             MethodHandle target, MethodHandle fallback) {
 *         // Note:  mcsInvoker is of type ()boolean.
 *         // Target and fallback may take any arguments, but must have the same type.
 *         return MethodHandles.guardWithTest(this.mcsInvoker, target, fallback);
 *     }
 *     public static void invalidateAll(SwitchPoint[] spts) {
 *         List<MutableCallSite> mcss = new ArrayList<>();
 *         for (SwitchPoint spt : spts)  mcss.add(spt.mcs);
 *         for (MutableCallSite mcs : mcss)  mcs.setTarget(K_false);
 *         MutableCallSite.syncAll(mcss.toArray(new MutableCallSite[0]));
 *     }
 * }
 * }</pre>
 * @author Remi Forax, JSR 292 EG
 */
public class SwitchPoint {
    private static final MethodHandle
        K_true  = MethodHandles.constant(boolean.class, true),
        K_false = MethodHandles.constant(boolean.class, false);

    private final MutableCallSite mcs;
    private final MethodHandle mcsInvoker;

    /**
     * 创建一个新的切换点。
     */
    public SwitchPoint() {
        this.mcs = new MutableCallSite(K_true);
        this.mcsInvoker = mcs.dynamicInvoker();
    }

    /**
     * 确定此切换点是否已被无效化。
     *
     * <p style="font-size:smaller;">
     * <em>讨论：</em>
     * 由于无效化的单向性质，一旦切换点开始
     * 为 {@code hasBeenInvalidated} 返回 true，
     * 它将在未来始终如此。
     * 另一方面，对其他线程可见的有效切换点可能
     * 由于另一个线程的请求而在任何时刻被无效化。
     * <p style="font-size:smaller;">
     * 由于无效化是全局和即时操作，
     * 在有效切换点上执行此查询
     * 必须与任何可能导致无效化的
     * 其他线程内部排序。
     * 因此此查询可能很昂贵。
     * 构建查询切换点 {@code s} 无效化状态的
     * 布尔值方法句柄的推荐方法是
     * 在 {@link MethodHandles#constant constant} true 和 false 方法句柄上
     * 调用 {@code s.guardWithTest}。
     *
     * @return 如果此切换点已被无效化则返回 true
     */
    public boolean hasBeenInvalidated() {
        return (mcs.getTarget() != K_true);
    }

    /**
     * 返回一个始终委托给目标或回退的方法句柄。
     * 只要切换点有效，方法句柄就会委托给目标。
     * 之后，它将永久委托给回退。
     * <p>
     * 目标和回退必须具有完全相同的方法类型，
     * 生成的组合方法句柄也将是此类型。
     *
     * @param target 只要切换点有效，切换点选择的方法句柄
     * @param fallback 切换点无效化后选择的方法句柄
     * @return 始终调用目标或回退的组合方法句柄
     * @throws NullPointerException 如果任一参数为 null
     * @throws IllegalArgumentException 如果两个方法类型不匹配
     * @see MethodHandles#guardWithTest
     */
    public MethodHandle guardWithTest(MethodHandle target, MethodHandle fallback) {
        if (mcs.getTarget() == K_false)
            return fallback;  // already invalid
        return MethodHandles.guardWithTest(mcsInvoker, target, fallback);
    }

    /**
     * 将所有给定的切换点设置为无效状态。
     * 此调用执行后，没有线程会观察到任何
     * 切换点处于有效状态。
     * <p>
     * 此操作可能很昂贵，应谨慎使用。
     * 如果可能，应该缓冲以便对切换点集合进行批处理。
     * <p>
     * 如果 {@code switchPoints} 包含 null 元素，
     * 将抛出 {@code NullPointerException}。
     * 在这种情况下，数组中的一些非 null 元素可能会
     * 在方法异常返回之前被处理。
     * 这些元素是哪些（如果有的话）取决于实现。
     *
     * <p style="font-size:smaller;">
     * <em>讨论：</em>
     * 出于性能原因，{@code invalidateAll} 不是
     * 单个切换点上的虚方法，而是应用于一组切换点。
     * 某些实现可能会为处理一个或多个无效化操作
     * 产生较大的固定开销成本，
     * 但每个额外无效化的增量成本较小。
     * 无论如何，此操作可能很昂贵，因为
     * 其他线程可能必须以某种方式被中断
     * 以便让它们注意到更新的切换点状态。
     * 但是，可以观察到单次调用无效化
     * 多个切换点与多次调用具有相同的正式效果，
     * 每次调用只针对其中一个切换点。
     *
     * <p style="font-size:smaller;">
     * <em>实现注意：</em>
     * {@code SwitchPoint} 的简单实现可能使用
     * 私有 {@link MutableCallSite} 来发布切换点的状态。
     * 在这样的实现中，{@code invalidateAll} 方法可以
     * 简单地更改调用站点的目标，并发出一次调用来
     * {@linkplain MutableCallSite#syncAll 同步}所有
     * 私有调用站点。
     *
     * @param switchPoints 要同步的调用站点数组
     * @throws NullPointerException 如果 {@code switchPoints} 数组引用为 null
     *                              或数组包含 null
     */
    public static void invalidateAll(SwitchPoint[] switchPoints) {
        if (switchPoints.length == 0)  return;
        MutableCallSite[] sites = new MutableCallSite[switchPoints.length];
        for (int i = 0; i < switchPoints.length; i++) {
            SwitchPoint spt = switchPoints[i];
            if (spt == null)  break;  // MSC.syncAll will trigger a NPE
            sites[i] = spt.mcs;
            spt.mcs.setTarget(K_false);
        }
        MutableCallSite.syncAll(sites);
    }
}
